package com.shape.sdk;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.shape.sdk.net.PushAddrResponse;
import com.shape.sdk.net.ShapeContants;
import com.shape.sdk.net.ShapeHttpClient;
import com.shape.sdk.protocol.Notification;
import com.shape.sdk.protocol.RegistReq;
import com.squareup.wire.Wire;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


class PushConnection {
  private String PUSH_SERVER = "123.56.228.13";
  private int PUSH_SERVER_PORT = 8088;
  private Handler handler;
  private Listener listener;
  private int storeId;
  private int stationId;
  private String hubId;

  interface Listener {
    void onNotification(Notification notification);
  }

  public PushConnection(int storeId, int stationId, String hubId) {
    this.storeId = storeId;
    this.stationId = stationId;
    this.hubId = hubId;
    handler = new Handler(Looper.getMainLooper());
  }

  private void registHub(DataOutputStream output) throws IOException {
    RegistReq.Builder req = new RegistReq.Builder();
    req.storeId = storeId;
    req.stationId = stationId;
    req.hubId = hubId;
    writeMessage(output, req.build());
  }

  private void writeMessage(DataOutputStream output, com.squareup.wire.Message message)
      throws IOException {
    byte[] data = message.toByteArray();
    output.writeInt(data.length);
    output.write(data);
    output.flush();
  }

  public void start(Listener listener) {
    this.listener = listener;
    connectServer();
  }

  private void connectServer() {
    new Thread(new Runnable() {
      @Override
      public void run() {
        final int defaultBufferSize = 1024;
        PushAddrResponse response = queryPushServerAddr(storeId);
        if (response == null) {
          Log.d("shapeInner", "no push server");
          reConnect();
          return;
        }
        final int defaultTimeout = 900;
        byte[] buffer = new byte[defaultBufferSize];
        Wire wire = new Wire();
        Socket socket = null;
        int timeout = defaultTimeout;
        try {
          socket = new Socket(response.getHostIp(), response.getPort());
          socket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(timeout));
        } catch (IOException e) {
          e.printStackTrace();
          reConnect();
          return;
        }

        Log.d("shapeInner", "push socket connected");
        try {
          DataInputStream input =
              new DataInputStream(new BufferedInputStream(socket.getInputStream()));
          DataOutputStream output =
              new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
          registHub(output);
          int len;
          while (true) {
            len = input.readInt();
            if (buffer.length < len) {
              buffer = new byte[len];
            }
            input.readFully(buffer, 0, len);

            Notification notification = null;
            try {
              notification = wire.parseFrom(buffer, 0, len, Notification.class);
              if (notification.type == ShapeContants.NOTIFICATION_TYPE_SYNC) {
                registHub(output);
              } else {
                listener.onNotification(notification);
              }
              Log.d("shapeInner", "push received");
            } catch (Error error) {
              Log.d("shapeInner", "push parse error");
              reConnect();
              return;
            }
          }
        } catch (IOException e) {
          e.printStackTrace();
        } catch (OutOfMemoryError e) {
          e.printStackTrace();
        } catch (IllegalArgumentException e) {
          e.printStackTrace();
        } catch (Throwable e) {
          e.printStackTrace();
        } finally {
          reConnect();
          if (socket != null) {
            try {
              socket.close();
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      }
    }, "PUSHCONNECTION_THREAD").start();
  }

  private void reConnect() {
    Log.d("shapeInner", "========push reconnect======");
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        connectServer();
      }
    }, 1000);
  }

  // 在工作线程上所以直接提供了同步请求
  private PushAddrResponse queryPushServerAddr(int storeId) {
    final Map<String, String> map = new HashMap<>();
    map.put("store_id", String.valueOf(storeId));
    ShapeHttpClient<PushAddrResponse> client = new ShapeHttpClient<>(PushAddrResponse.class);
    return client.requestGet(ShapeContants.QUERY_PUSH_SERVER_PORT, map);
  }

}
